/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ru.ssau.presenter;

import ru.ssau.lib.AppendableObjectOutputStream;
import ru.ssau.lib.Request;
import ru.ssau.lib.AppendableObjectInputStream;
import java.io.IOException;
import java.net.Socket;
import org.jdom.Element;
import ru.ssau.entities.Message;
import ru.ssau.entities.User;
import ru.ssau.lib.Response;
import ru.ssau.lib.StringIntegerPair;
import ru.ssau.lib.XMLizable;
import ru.ssau.model.DataModel;
import ru.ssau.model.DataModelChangeEvent;
import ru.ssau.model.DataModelChangeListener;
import ru.ssau.model.storing.DataAccessException;
import ru.ssau.utils.XMLHelper;


public class ServerPresenter implements DataModelChangeListener {

    private Socket socket;
    private AppendableObjectInputStream in;
    private AppendableObjectOutputStream out;
    private boolean isModelLocker = false;
    private DataModel dataModel;

    public ServerPresenter(Socket socket, DataModel dataModel) throws IOException {
        this.socket = socket;
        this.dataModel = dataModel;

        in = new AppendableObjectInputStream(socket.getInputStream());
        out = new AppendableObjectOutputStream(socket.getOutputStream());
    }

    public void service() throws IOException, ClassNotFoundException {
        try {
            while (socket.isConnected()) {
                Request action = (Request) receiveFromClient();
                //Добавление сущности
                if (action.getAction() == Request.ADD) {
                    if (action.getEntityType() == Request.MESSAGE) {
                        Message message = (Message) action.getEntity();
                        if (message != null) {
                            dataModel.addMessage(message);
                        }
                    } else if (action.getEntityType() == Request.USER) {
                        User user = (User) action.getEntity();
                        if (user != null) {
                            dataModel.addUser(user);
                        }
                    } else if(action.getEntityType() == Request.THREAD) {
                        ru.ssau.entities.Thread thread =
                                (ru.ssau.entities.Thread) action.getEntity();
                        if(thread != null) {
                            dataModel.addThread(thread);
                        }
                    }
                    //Разблокирование модели
                    isModelLocker = false;
                    dataModel.unlock();
                }
                //SET - установки текущей (отображаемой) сущности в представлении
                //GET - получение сущности по id
                else if((action.getAction() == Request.SET) || (action.getAction() == Request.GET)) {
                    Response response = new Response();
                    if(action.getAction() == Request.SET) {
                        response.setAction(Response.SET);
                    }
                    else {
                        response.setAction(Response.GET);
                    }
                    if(action.getEntityType() == Request.MESSAGE) {
                        response.setEntityType(Response.MESSAGE);
                        response.setMessage(dataModel.getMessageById(action.getId()));
                    } else if(action.getEntityType() == Request.USER) {
                        response.setEntityType(Response.USER);
                        response.setUser(dataModel.getUserById(action.getId()));
                    } else if(action.getEntityType() == Request.THREAD) {
                        response.setEntityType(Response.THREAD);
                        response.setThread(dataModel.getThreadById(action.getId()));
                    }
                    sendToClient(response);
                }
                //Замена старой сущности на новую
                else if(action.getAction() == Request.REPLACE) {
                    if(action.getEntityType() == Request.MESSAGE) {
                        Message message = (Message) action.getEntity();
                        if(message != null) {
                            dataModel.replaceMessage(message);
                        }
                    } else if(action.getEntityType() == Request.USER) {
                        User user = (User) action.getEntity();
                        if(user != null) {
                            dataModel.replaceUser(user);
                        }
                    } else if(action.getEntityType() == Request.THREAD) {
                        ru.ssau.entities.Thread thread = (ru.ssau.entities.Thread) action.getEntity();
                        if(thread != null) {
                            dataModel.replaceThread(thread);
                        }
                    }
                    isModelLocker = false;
                    dataModel.unlock();
                }
                //Удаление сущности
                else if (action.getAction() == Request.DELETE) {
                    if(action.getEntityType() == Request.MESSAGE) {
                        dataModel.deleteMessageById(action.getId());
                    } else if(action.getEntityType() == Request.USER) {
                        dataModel.deleteUserById(action.getId());
                    } else if(action.getEntityType() == Request.THREAD){
                        dataModel.deleteThreadById(action.getId());
                    }
                    //Разблокирование модели
                    isModelLocker = false;
                    dataModel.unlock();
                }
                //Отклик на запрос об изменении сущности
                else if (action.getAction() == Request.CHANGE) {
                    isModelLocker = dataModel.tryLock();
                    Response response = new Response();
                    response.setAction(Response.CHANGE);
                    response.setCanChangeModel(isModelLocker);
                    sendToClient(response);
                } else if(action.getAction() == Request.STATE_NOTIFICATION) {
                    Response r = new Response();
                    r.setAction(Response.STATE_NOTIFICATION);
                    r.setMessages(StringIntegerPair.toStringIntegerPairArray(dataModel.getMessages()));
                    r.setThreads(StringIntegerPair.toStringIntegerPairArray(dataModel.getThreads()));
                    r.setUsers(StringIntegerPair.toStringIntegerPairArray(dataModel.getUsers()));
                    sendToClient(r);
                }
            }
        } catch(DataAccessException e) {
            e.printStackTrace();
        } finally {
            if (isModelLocker) {
                dataModel.unlock();
            }
            dataModel.removeDataModelChangeListener(this);
        }
    }

    /**
     * Обработка событий модели
     * @param event - событие, сгенерированное моделью
     */
    public void modelChanged(DataModelChangeEvent event) {
        try {
            Response response = new Response();
            if (event.getAction() == DataModelChangeEvent.ADD) {
                response.setAction(Response.ADD);
                if (event.getEntityType() == DataModelChangeEvent.MESSAGE) {
                    response.setEntityType(Response.MESSAGE);
                    response.setMessageListElement(dataModel.getMessageById(event.getId()).toStringIntegerPair());
                } else if(event.getEntityType() == DataModelChangeEvent.USER) {
                    response.setEntityType(Response.USER);
                    response.setUserListElement(dataModel.getUserById(event.getId()).toStringIntegerPair());
                } else if(event.getEntityType() == DataModelChangeEvent.THREAD) {
                    response.setEntityType(Response.THREAD);
                    response.setThreadListElement(dataModel.getThreadById(event.getId()).toStringIntegerPair());
                }
            } else if(event.getAction() == DataModelChangeEvent.DELETE) {
                response.setAction(Response.DELETE);
                if(event.getEntityType() == DataModelChangeEvent.MESSAGE) {
                    response.setEntityType(Response.MESSAGE);
                    response.setMessages(StringIntegerPair.toStringIntegerPairArray(dataModel.getMessages()));
                } else if(event.getEntityType() == DataModelChangeEvent.USER) {
                    response.setEntityType(Response.USER);
                    response.setUsers(StringIntegerPair.toStringIntegerPairArray(dataModel.getUsers()));
                    response.setMessages(StringIntegerPair.toStringIntegerPairArray(dataModel.getMessages()));
                    out.reset(); // Я три дня потратил на то, чтобы написать эту строку!!!
                } else if(event.getEntityType() == DataModelChangeEvent.THREAD) {
                    response.setEntityType(Response.THREAD);
                    response.setThreads(StringIntegerPair.toStringIntegerPairArray(dataModel.getThreads()));
                    response.setMessages(StringIntegerPair.toStringIntegerPairArray(dataModel.getMessages()));
                }
            } else if(event.getAction() == DataModelChangeEvent.REPLACE) {
                response.setAction(Response.REPLACE);
                if(event.getEntityType() == DataModelChangeEvent.MESSAGE) {
                    response.setEntityType(Response.MESSAGE);
                    Message m = dataModel.getMessageById(event.getId());
                    response.setMessageListElement(m.toStringIntegerPair());
                    response.setMessage(dataModel.getMessageById(event.getId()));
                    response.setMessages(StringIntegerPair.toStringIntegerPairArray(dataModel.getMessages()));
                } else if(event.getEntityType() == DataModelChangeEvent.USER) {
                    response.setEntityType(Response.USER);
                    User u = dataModel.getUserById(event.getId());
                    response.setUserListElement(u.toStringIntegerPair());
                    response.setUsers(StringIntegerPair.toStringIntegerPairArray(dataModel.getUsers()));
                } else if(event.getEntityType() == DataModelChangeEvent.THREAD) {
                    response.setEntityType(Response.THREAD);
                    ru.ssau.entities.Thread t = dataModel.getThreadById(event.getId());
                    response.setThreadListElement(t.toStringIntegerPair());
                    response.setThreads(StringIntegerPair.toStringIntegerPairArray(dataModel.getThreads()));
                }
            }
            sendToClient(response);

        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (DataAccessException ex) {
            ex.printStackTrace();
        }

    }

    private Object receiveFromClient() throws IOException, ClassNotFoundException {
        Request request=new Request();
        Element root=XMLHelper.parseString(in.readUTF());
        request.loadFromXMLElement(root);
        return request;
//        return in.readObject();
    }

    private void sendToClient(Object obj) throws IOException {
        //out.writeUnshared(obj);
        XMLizable xObj=(XMLizable)obj;
        out.writeUTF(XMLHelper.outputXML(xObj.toXMLElement()));
        out.flush();
    }

    public void closeConnection() throws IOException {
        if (in != null) {
            in.close();
        }
        if (out != null) {
            out.close();
        }
        if (socket != null) {
            socket.close();
        }

        dataModel.removeDataModelChangeListener(this);
    }
}
